using System;
using System.Collections.Generic;
using System.Text;
using System.IO;
using MD5Hash = System.Security.Cryptography.MD5;
using HashAlgorithm = System.Security.Cryptography.HashAlgorithm;


namespace Zsa.Emulation.Cartridge
{
    /// <summary>
    /// Base class for cart dump reader/writer
    /// </summary>
    public abstract class CartDumpReaderWriter
    {
        #region Fields
        /// <summary>
        /// Initializes a new instance of the <see cref="CartDumpReader"/> class.
        /// </summary>
        protected CartDumpReaderWriter(string formatName)
        {
        }
        #endregion Fields

        #region Methods
        /// <summary>
        /// Trys to get the cart information.
        /// </summary>
        /// <param name="stream">The filestream to the cart.</param>
        /// <param name="cartInfo">The cart information.</param>
        /// <returns>true if able to read cart info.</returns>
        public abstract bool TryGetInfo(FileStream stream, out CartridgeFileInfo cartInfo);

        /// <summary>
        /// Writes the cartrige to the stream.
        /// </summary>
        /// <param name="cart">The cart.</param>
        /// <param name="stream">The stream to write to.</param>
        public void Write(Cart cart, Stream stream)
        {
            //Check the args.
            if (cart == null)
            {
                throw new ArgumentNullException("cart", "The cart cannot be null !");
            }
            if (stream == null)
            {
                throw new ArgumentNullException("stream", "The stream cannot be null");
            }
            if (cart.CartridgeStream == stream)
            {
                throw new ArgumentException("Cannot read and write to the same stream !");
            }
            if (!stream.CanWrite)
            {
                throw new ArgumentException("Cannot write to the output stream !");
            }
            //Call the abstract method.
            OnWrite(cart, stream);
            
        }

        protected abstract void OnWrite(Cart cart, Stream stream);


        /// <summary>
        /// Trys to load a cart.
        /// </summary>
        /// <param name="stream">The stream for the cart.</param>
        /// <returns>The loaded cart.</returns>
        public abstract Cart Load(FileStream stream);


        /// <summary>
        /// Creates the an md5 hash for the cart.
        /// </summary>
        /// <param name="stream">The stream.</param>
        /// <returns></returns>
        public static Guid CreateMD5(FileStream stream)
        {
            MD5Hash _MD5 = MD5Hash.Create();
            return new Guid(_MD5.ComputeHash(stream));
        }



        /// <summary>
        // This method accepts two strings the represent two files to 
        // compare. A return value of 0 indicates that the contents of the files
        // are the same. A return value of any other value indicates that the 
        // files are not the same.
        // This method was copied from msdn.
        /// </summary>
        /// <param name="file1">The file1.</param>
        /// <param name="file2">The file2.</param>
        /// <returns></returns>
        public static bool FileBinaryCompare(string file1, string file2)
        {
            
            int file1byte;
            int file2byte;
            FileStream fs1;
            FileStream fs2;

            // Determine if the same file was referenced two times.
            if (file1 == file2)
            {
                // Return true to indicate that the files are the same.
                return true;
            }

            // Open the two files.
            fs1 = new FileStream(file1, FileMode.Open);
            fs2 = new FileStream(file2, FileMode.Open);

            // Check the file sizes. If they are not the same, the files 
            // are not the same.
            if (fs1.Length != fs2.Length)
            {
                // Close the file
                fs1.Close();
                fs2.Close();

                // Return false to indicate files are different
                return false;
            }

            // Read and compare a byte from each file until either a
            // non-matching set of bytes is found or until the end of
            // file1 is reached.
            do
            {
                // Read one byte from each file.
                file1byte = fs1.ReadByte();
                file2byte = fs2.ReadByte();
            }
            while ((file1byte == file2byte) && (file1byte != -1));

            // Close the files.
            fs1.Close();
            fs2.Close();

            // Return the success of the comparison. "file1byte" is 
            // equal to "file2byte" at this point only if the files are 
            // the same.
            return ((file1byte - file2byte) == 0);
        }

        #endregion Methods
    }
}
